using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Forms;

namespace VSSDKAssist.Recipes.Common.Editors.Grid
{
    /// <summary/>
    public class DataGridViewBinding<T> where T : new()
    {
        private bool bound = false;
        private DataGridView dataGridView;
        private IList<T> boundList;

        /// <summary/>
        public event EventHandler DataChanged;

        /// <summary>
        /// Binds the specified data grid view.
        /// </summary>
        /// <param name="dataGridView">The data grid view.</param>
        /// <param name="boundList">The bound list.</param>
        public void Bind(DataGridView dataGridView, IList<T> boundList)
        {
            this.dataGridView = dataGridView;
            this.boundList = boundList;

            foreach(DataGridViewColumn column in dataGridView.Columns)
            {
                PropertyDescriptor propDescr = TypeDescriptor.GetProperties(typeof(T))[column.DataPropertyName];
                column.ValueType = propDescr.PropertyType;
                DesignerColumn designerColumn = column as DesignerColumn;
                if(designerColumn != null)
                {
                    designerColumn.PropertyDescriptor = propDescr;
                }
            }

            if(bound)
                UnwireEvents();

            WireEvents();
            bound = true;
        }

        private void UnwireEvents()
        {
            dataGridView.UserAddedRow -= OnUserAddedRow;
            dataGridView.UserDeletingRow -= OnUserDeletingRow;
            dataGridView.CellValueChanged -= OnCellValueChanged;
            dataGridView.DataError -= OnCellDataError;
        }

        private void WireEvents()
        {
            dataGridView.UserAddedRow += OnUserAddedRow;
            dataGridView.UserDeletingRow += OnUserDeletingRow;
            dataGridView.CellValueChanged += OnCellValueChanged;
            dataGridView.DataError += OnCellDataError;
        }

        private void OnUserAddedRow(object sender, DataGridViewRowEventArgs e)
        {
            T item = new T();
            boundList.Add(item);

            foreach(DataGridViewCell cell in dataGridView.Rows[e.Row.Index - 1].Cells)
            {
                if(cell.Value != null && !string.IsNullOrEmpty(cell.OwningColumn.DataPropertyName))
                {
                    PropertyInfo prop = typeof(T).GetProperty(cell.OwningColumn.DataPropertyName);
                    prop.SetValue(item, cell.Value, null);
                }
            }
            FireDataChanged();
        }

        private void OnUserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)
        {
            boundList.RemoveAt(e.Row.Index);
            FireDataChanged();
        }

        private void OnCellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            if(e.RowIndex < boundList.Count)
            {
                DataGridViewColumn column = dataGridView.Columns[e.ColumnIndex];
                if(!string.IsNullOrEmpty(column.DataPropertyName))
                {
                    T item = boundList[e.RowIndex];
                    if(Validation.ExecuteConfigurationValidatorAttributes(dataGridView[e.ColumnIndex, e.RowIndex], typeof(T), dataGridView.Site))
                    {
                        PropertyInfo prop = typeof(T).GetProperty(column.DataPropertyName);
                        prop.SetValue(item, dataGridView[e.ColumnIndex, e.RowIndex].Value, null);
                    }
                }
            }

            FireDataChanged();
        }

        private void OnCellDataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            if(e.Exception != null)
            {
                //IUIService svc = (IUIService)this.GetsGetSercice
                MessageBox.Show(e.Exception.Message,
                    "Error", MessageBoxButtons.OK, MessageBoxIcon.Error,
                    MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
                e.ThrowException = false;
            }
        }

        private void FireDataChanged()
        {
            if(DataChanged != null)
                DataChanged(this, EventArgs.Empty);
        }
    }
}
